package network

import (
	"errors"
	"github.com/Unixeno/TheMoon/logging"
	"github.com/Unixeno/TheMoon/moon/config"
	"github.com/vishvananda/netlink"
	"net"
	"sync"
)

var manager *IPManager
var logger = logging.GetLogger("network")

var (
	Conflict       = errors.New("can't create network with the same name on different subnet")
	NotImplement   = errors.New("not implement")
	InvalidOptions = errors.New("network options are invalid")
)

type Network interface {
	Type() string     // 获取该接口的类型
	Addr() *net.IPNet // 获取该接口的网络地址

	Setup() error  // 配置网络
	Remove() error // 删除此网络

	Release(ip net.IP) error                          // 释放一个 ip
	Allocate() (ip net.IP, gateway net.IP, err error) // 分配一个 IP
}

type Options interface {
	IP() net.IP
	Mask() net.IPMask
}

var localNetwork sync.Map // 本地网络设备

// 获取一个已创建的网络
func GetNetwork(name string) Network {
	network, ok := localNetwork.Load(name)
	if ok {
		return network.(Network)
	} else {
		return nil
	}
}

// 删除一个网络
func DeleteNetwork(name string) error {
	network, ok := localNetwork.Load(name)
	if ok {
		localNetwork.Delete(name)
		return network.(Network).Remove()
	} else {
		return nil
	}
}

// 创建一个网络，此函数保证幂等性，当存在同名接口时会判断接口类型
func CreateNetwork(networkType string, name string, opt Options) (Network, error) {
	if link, err := netlink.LinkByName(name); err != nil {
		if _, ok := err.(netlink.LinkNotFoundError); !ok {
			return nil, err
		}
		// 网络设备不存在，继续创建流程
	} else {
		// 网络设备已存在则判断其网段是否相同，当前仅判断IPv4
		addr, _ := netlink.AddrList(link, netlink.FAMILY_V4)
		if len(addr) == 1 {
			mask1Ones, mask1Bits := addr[0].Mask.Size()
			mask2Ones, mask2Bits := opt.Mask().Size()

			if addr[0].IP.Equal(opt.IP()) && mask1Ones == mask2Ones && mask1Bits == mask2Bits {
				// 存在相同地址的网络，忽略本次创建
				logger.Debug("network already exist")
				network := &bridgeNetwork{
					name:   name,
					addr:   addr[0].IPNet,
					method: BridgeMethodNat,
				}
				localNetwork.Store(name, network)
				manager.Register(name, *network.addr)
				return nil, nil
			}
		}
		return nil, Conflict
	}

	var network Network
	switch networkType {
	case "bridge":
		brOpt, ok := opt.(*BridgeNetworkOptions)
		if !ok {
			return nil, InvalidOptions
		}
		network = createBridgeNetwork(name, brOpt)
		localNetwork.Store(name, network)
	default:
		return nil, NotImplement
	}

	return network, network.Setup()
}

func InitNetwork() {
	// ip forward
	enableIpv4Forward()
	// 初始化 Manager
	manager = InitManager()
	// 建立默认 network, nat
	err := CreateDefaultNetwork()
	if err != nil {
		logger.WithError(err).Error("failed to create default network")
		return
	}
}

func CreateDefaultNetwork() error {
	logger.Debug("create default network " + config.SysConfig.Zone.DefaultNetworkName)
	_, ipNet, err := net.ParseCIDR(config.SysConfig.Zone.DefaultNetwork)
	if err != nil {
		return err
	}
	// 默认网络使用网桥创建
	defaultNetworkOpt := &BridgeNetworkOptions{
		Addr: &net.IPNet{
			IP:   getGateway(ipNet),
			Mask: ipNet.Mask,
		},
		Method: BridgeMethodNat,
	}
	if _, err := CreateNetwork("bridge", config.SysConfig.Zone.DefaultNetworkName, defaultNetworkOpt); err != nil {
		return err
	}
	return nil
}
